#include "../config.h"

#include <glibmm/i18n.h>
#include <glib/gstdio.h>

#include "application.h"
#include "icon.h"
#include "paths.h"
#include "widgets.h"

namespace gpyp {

namespace {
#include "icons/icon_incl.h"
}

//========================
// IconManager
#include "icons/icon_impl.cpp"

const char *const IconManager::BUILTIN_ICON_LIST[] = {
#include "icons/icon_builtin_icon_list.h"
};

GtkIconSize IconManager::ICON_SIZE_16 = (GtkIconSize)-1;
GtkIconSize IconManager::ICON_SIZE_32 = (GtkIconSize)-1;
GtkIconSize IconManager::ICON_SIZE_48 = (GtkIconSize)-1;

const char *IconManager::SPECIAL_WEBBROWSER = "gpyp-webbrowser";
const char *IconManager::SPECIAL_BBSBROWSER = "gpyp-bbsbrowser";

bool IconManager::is_special_icon(const Glib::ustring &name) {
  return g_str_has_prefix(name.c_str(), "gpyp-") != FALSE;
}

sigc::signal<void, const char *> IconManager::_signal_special_icon_changed;

Glib::ustring                IconManager::_icondir_user;
Glib::RefPtr<Gtk::IconTheme> IconManager::_icontheme_user;

Glib::ustring IconManager::_sa_webbrowser;
Glib::ustring IconManager::_sa_bbsbrowser;

void IconManager::init(OptionContext &opts) {
#include "icons/icon_add_builtin_icon.cpp"

  ICON_SIZE_16 = gtk_icon_size_register("gpyp_16", 16, 16);
  ICON_SIZE_32 = gtk_icon_size_register("gpyp_32", 32, 32);
  ICON_SIZE_48 = gtk_icon_size_register("gpyp_48", 48, 48);

  _icondir_user = Glib::build_filename(opts.get_config_dir(), "icons");

  const char *path_user[] = {_icondir_user.c_str()};
  _icontheme_user = Gtk::IconTheme::create();
  _icontheme_user->set_search_path(Glib::ArrayHandle<Glib::ustring>(path_user, 1, Glib::OWNERSHIP_NONE));

  Glib::RefPtr<Gtk::IconTheme> def = Gtk::IconTheme::get_default();
  def->prepend_search_path(_icondir_user);
  def->append_search_path(PKGPIXMAPS_16_DIR);
  def->append_search_path(PKGPIXMAPS_22_DIR);
  def->append_search_path(PKGPIXMAPS_SCALABLE_DIR);
}

void IconManager::add_icon_file(const Glib::ustring &file_path) {
  std::string srcpath = Glib::filename_from_utf8(file_path);
  std::string name    = Glib::path_get_basename(srcpath);
  std::string dstdir  = Glib::filename_from_utf8(_icondir_user);
  std::string dstpath = Glib::build_filename(dstdir, name);

  if (!Glib::file_test(dstdir, Glib::FILE_TEST_EXISTS) &&
      g_mkdir_with_parents(dstdir.c_str(), 0755)) {
    log::err << format(_("failed to create directory - \"%s\""),
                       _icondir_user.c_str())
             << log::endl;
  }
  if (!Glib::file_test(dstdir, Glib::FILE_TEST_IS_DIR)) {
    log::err << format(_("\"%s\" is not directory"), _icondir_user.c_str())
             << log::endl;
    return;
  }

  FILE *src = fopen(srcpath.c_str(), "rb");
  if (!src) {
    log::err << _("failed to open file to read") << log::endl;
    return;
  }
  FILE *dst = fopen(dstpath.c_str(), "wb");
  if (!dst) {
    log::err << _("failed to open file to write") << log::endl;
    fclose(src);
    return;
  }

  char  *buf = new char[10 * 1024];
  size_t size = 0;
  while((size = fread(buf, 1, 10 * 1024, src)) > 0) {
    char *p = buf, *end = buf + size;
    while(p < end) {
      size_t wsize = fwrite(buf, 1, end - p, dst);
      p += wsize;
    }
  }
  fclose(src);
  fclose(dst);

  Gtk::IconTheme::get_default()->rescan_if_needed();
  _icontheme_user->rescan_if_needed();
}
bool IconManager::remove_icon_file(const Glib::ustring &name) {
  try {
    Gtk::IconInfo info =
      _icontheme_user->lookup_icon(name, 16, (Gtk::IconLookupFlags)0);
    if (info) {
      std::string path = Glib::filename_from_utf8(info.get_filename());
      if (!g_remove(path.c_str())) {
        Gtk::IconTheme::get_default()->rescan_if_needed();
        _icontheme_user->rescan_if_needed();
        return true;
      }
    }
  } catch(const Glib::Exception &ex) {
    log::err << ex.what() << log::endl;
  }
  return false;
}

Glib::RefPtr<Gdk::Pixbuf>
IconManager::load_icon(const Glib::ustring &icon_name,
                       int                  size,
                       GtkIconSize          icon_size,
                       Gtk::IconLookupFlags flags) {
  if (icon_name.empty()) return Glib::RefPtr<Gdk::Pixbuf>();

  GtkIconSet *icon_set = gtk_icon_factory_lookup_default(icon_name.c_str());
  if (icon_set) {
    GdkPixbuf *pixbuf =
      gtk_icon_set_render_icon(icon_set,
                               gtk_widget_get_default_style(),
                               gtk_widget_get_default_direction(),
                               GTK_STATE_NORMAL,
                               icon_size,
                               NULL, /* GtkWidget *widget */
                               NULL);
    if (pixbuf) return Glib::wrap(pixbuf);
  }

  const char *alias = NULL;
  if (icon_name == SPECIAL_WEBBROWSER) {
    if (_sa_webbrowser.empty()) {
      alias = WEBBROWSER;
    } else {
      alias = _sa_webbrowser.c_str();
    }
  } else if (icon_name == SPECIAL_BBSBROWSER) {
    if (_sa_bbsbrowser.empty()) {
      alias = "";
    } else {
      alias = _sa_bbsbrowser.c_str();
    }
  }
  if (alias) {
    try {
      if (alias[0]) {
        return Gtk::IconTheme::get_default()->load_icon(alias, size, flags);
      }
    } catch(...) { }
    return Glib::RefPtr<Gdk::Pixbuf>();
  } else {
    try {
      return Gtk::IconTheme::get_default()->load_icon(icon_name, size, flags);
    } catch(...) { }
    if (Glib::file_test(icon_name, Glib::FILE_TEST_IS_REGULAR)) {
      try {
        return Gdk::Pixbuf::create_from_file(icon_name, size, size);
      } catch(...) { }
    }
  }
  return Glib::RefPtr<Gdk::Pixbuf>();
}

bool IconManager::has_icon(const Glib::ustring &icon_name) {
  return Gtk::IconTheme::get_default()->has_icon(icon_name);
}

Glib::ArrayHandle<int>
IconManager::get_icon_sizes(const Glib::ustring &icon_name) {
  return Gtk::IconTheme::get_default()->get_icon_sizes(icon_name);
}

void IconManager::webbrowser_on_changed() {
  Glib::ustring name =
    shell_command_get_name(*app_.conf().command()->webbrowser()->command());
  if (!name.empty() && has_icon(name)) {
    _sa_webbrowser = name;
  } else {
    _sa_webbrowser.clear();
  }
  _signal_special_icon_changed(SPECIAL_WEBBROWSER);
}
void IconManager::bbsbrowser_on_changed() {
  Glib::ustring name =
    shell_command_get_name(*app_.conf().command()->bbsbrowser()->command());
  if (!name.empty() && has_icon(name)) {
    _sa_bbsbrowser = name;
  } else {
    _sa_bbsbrowser.clear();
  }
  _signal_special_icon_changed(SPECIAL_BBSBROWSER);
}

IconManager::IconManager(Application &app) :
  app_(app)
{
  webbrowser_on_changed();
  bbsbrowser_on_changed();

  ConfMain::RefBuiltInCommand command = app_.conf().command();
  command->webbrowser()->command()->signal_changed()
    .connect(sigc::mem_fun(*this, &IconManager::webbrowser_on_changed));
  command->bbsbrowser()->command()->signal_changed()
    .connect(sigc::mem_fun(*this, &IconManager::bbsbrowser_on_changed));
}

}/* namespace gpyp */
